package com.tianbao54.netty.handler;


import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;

public class NettyServerHandler extends SimpleChannelInboundHandler<String> {

    private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    private static final Map<ChannelId,String> usernameMapped = new HashMap<>();
    private static final Map<ChannelId,ChannelId> contractUser = new HashMap<>();
    private static final StringBuffer buffer = new StringBuffer ();

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        final boolean[] flag = {false};
        usernameMapped.entrySet().stream().filter(channelId -> Objects.equals(channelId.getKey(),ctx.channel().id()))
                .filter(entry -> Objects.isNull(entry.getValue()))
                .findFirst().ifPresent(entry -> {
                flag[0] = true;entry.setValue(msg);});

        if (!flag[0]) {
            if (contractUser.containsKey(ctx.channel().id())) {
                getChannelByChannelId(contractUser.get(ctx.channel().id())).writeAndFlush(structureString("[private/"+usernameMapped.get(ctx.channel().id())+"] " + msg));
            } else if (contractUser.containsValue(ctx.channel().id())) {
                getChannelByChannelId(getKeyByValue(ctx.channel().id())).writeAndFlush(structureString("[private/"+usernameMapped.get(ctx.channel().id())+"] " + msg));
            } else if (msg.indexOf("connect") != -1) {
                String contract = msg.split("connect")[1].trim();
                if (usernameMapped.containsValue(contract)) {
                    getChannelByUserNameAndWrite(contract,"["+usernameMapped.get(ctx.channel().id())+"] 想与你建立连接，是否同意 y/n ");
                    ctx.channel().writeAndFlush(structureString("正在与[" + contract + "] 建立连接..."));
                    contractUser.put(getChannelByUserName(contract).id(),ctx.channel().id());
                }
            } else if (contractUser.containsKey(ctx.channel().id())) {

                if (msg.indexOf("y") != -1) {
                    ctx.channel().writeAndFlush(structureString("与对方连接建立..."));
                    getChannelByChannelId(contractUser.get(ctx.channel().id())).writeAndFlush(structureString("与对方连接建立..."));
                } else {
                    ctx.channel().writeAndFlush(structureString("已经拒绝与对方连接..."));
                    getChannelByChannelId(contractUser.get(ctx.channel().id())).writeAndFlush(structureString("对方拒绝建立连接..."));
                    contractUser.remove(ctx.channel().id());
                }
            } else {
                channels.stream().filter(channel -> !Objects.equals(channel, ctx.channel())).forEach(channel -> channel.writeAndFlush(structureString("[" + usernameMapped.get(ctx.channel().id()) + "] " + msg)));
            }
        } else {
            channels.stream().filter(channel -> !Objects.equals(channel,ctx.channel())).forEach((channel) -> channel.writeAndFlush(structureString("[server] " + usernameMapped.get(ctx.channel().id()) + " is online now")));
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {

        channels.forEach((channel) -> channel.writeAndFlush(structureString("[server] " + usernameMapped.get(ctx.channel().id()) + " is offline now ")));
        channels.remove(ctx.channel());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        buffer.append("当前在线的人有：").append(usernameMapped.values()).append("\n");
        buffer.append("请输入您的用户名，以便其它用户和您联系：");
        ctx.channel().writeAndFlush(buffer.toString());
        usernameMapped.put(ctx.channel().id(),null);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        channels.add(ctx.channel());
    }

    private ChannelId getKeyByValue (ChannelId value) {
         Map.Entry<ChannelId,ChannelId> channelIdEntry = contractUser.entrySet().stream().filter(entry -> Objects.equals(entry.getValue(),value)).findFirst().orElse(null);
         return channelIdEntry.getKey();
    }

    private Channel getChannelByChannelId (ChannelId channelId) {

        return channels.stream().filter(channel -> Objects.equals(channel.id(),channelId)).findFirst().orElse(null);
    }


    private Channel getChannelByUserName (String username) {

        return usernameMapped.entrySet().stream().filter(entry -> Objects.equals(username,entry.getValue()))
                .flatMap(entry -> channels.stream().filter(channel -> Objects.equals(entry.getKey(),channel.id())))
                .findFirst().orElse(null);
    }

    private void getChannelByUserNameAndWrite (String username,String msg) {

         usernameMapped.entrySet().stream().filter(entry -> Objects.equals(username,entry.getValue()))
                .flatMap(entry -> channels.stream().filter(channel -> Objects.equals(entry.getKey(),channel.id())))
                .findFirst().ifPresent(channel -> channel.writeAndFlush(msg));
    }

    private String structureString (String ... str) {
        buffer.setLength(0);
        Stream.of(str).forEach(buffer::append);
        return buffer.append("\n").toString();
    }
}